/*  IR Translator code.
     Copyright 2010-11, Alan Chatham

	This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      
    This code takes in data from a IR reciever module, assembles the bits into bytes,
	then provides read access to those bytes via I2C.
*/

/*
Version 1.1

DonP Notes
20110125	Working copy of folder created.  Let's call this v1.0
20110125	Error code should be 0xff not 0x01, corrected
20110128	Added code to clear out "Bad packet" error flag upon read (otherwise it persists until a good packet arrives)
			Removed a couple warnings, edited out meaningless comparisons.
			Made decoding more timing compliant -- see in main() ~line 360 and ~390
*/

#include <avr/eeprom.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "usiTwiSlave.h"

///
/// Macros and defines to control various things
///

#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

// This is the address in EEPROM where we're storing our secondary I2C address
#define EEPROM_ADDRESS 0

// This is the pin the IR decoder is on
#define IR_SENSOR_PIN 1
// And the pin that lets the master know that data is ready
#define DATA_READY_PIN 4

// This many IR packets will be buffered
#define IR_BUFFER_SIZE 3
// The plus one here is for the size byte
#define IR_PACKET_MAX_LENGTH 9 + 1

///
///   Global Variables
///

// This is where we store the data coming in from the IR sensor.
// We have a 6 message buffer, with messages all being 10 bytes,
// the first byte being the message length and the next 2-9 bytes
// being the message itself.
static int8_t IR_Data_Buffer_Size = 0;
uint8_t IR_Data_Buffer[IR_BUFFER_SIZE][IR_PACKET_MAX_LENGTH];
static uint8_t IR_Data_Head = 0;
static uint8_t IR_Data_Tail = 0;

// This flag gets set if there is an error decoding a packet
static uint8_t Error_Flag = 0;

// These global variables help keep track of the time
static uint8_t millisecondsLow = 0;
static uint8_t millisecondsHigh = 0;
// And these ones hold values after a read, so we can tell exactly how many ms between
// when the data is recorded and when the data is sent over TWI
static uint8_t millisecondsLowHold = 0;
static uint8_t millisecondsHighHold = 0;

// Flag that tells us the IR data packet type - short or long
static uint8_t isShortPacket = 0;
// Length of the long packet we're looking at
static uint8_t longPacketLength = 0;
// These hold onto values related to the IR decoding process
static uint8_t previousIRPinState = 1;
// Keeps track of how often counter1 has overflown,
// which it does at 300us intervals
static uint16_t counter1Cycles = 0; 
// A buffer to build frames in
static uint8_t FrameBuilderBuffer[9] = {0,0,0,0,0, 0,0,0,0};
// And a variable to store that index in
static int16_t dataIndex = -1;	



// Simple functions to advance the head and tail of the internal ring buffer
// Increments the head or tail, and when it gets to the end of the buffer,
// wraps around to zero
inline static void advanceIRDataHead()
{
	IR_Data_Head++;
	if (IR_Data_Head >= IR_BUFFER_SIZE)
		IR_Data_Head = 0;
	return;
}
inline static void advanceIRDataTail()
{
	IR_Data_Tail++;
	if (IR_Data_Tail >= IR_BUFFER_SIZE)
		IR_Data_Tail = 0;
	return;
}
// This function tells us if our buffer is full or not.
inline static int isBufferFull()
{
	// If the head is one unit short of the tail, the buffer is full
	if ( ( (IR_Data_Tail == 0) && (IR_Data_Head == IR_BUFFER_SIZE - 1) ) ||
	     (IR_Data_Tail == IR_Data_Head + 1) )
		return 1;
	else
		return 0;
}

// This sets up the timer we're using for keeping track of the time
// It overflows every 1ms
inline static void setupTimer0()
{
	// Set the prescaler to 64 - so each tick is 8us
	TCCR0B |= (1<<CS00) | (1<<CS01);
	
	// Set the CTC so we automatically compare...
	TCCR0A |= (1<<WGM01);
	// Set the CTC compare value - 125 X 8us is 1ms
	OCR0A = 125;
}

// This sets up the timer we're using to time the incoming IR pulses
// It overflows every 300us
inline static void setupTimer1()
{
	// Set the prescaler to 32 - so each tick is 4us
	TCCR1 |= (1<<CS12) | (1<<CS11);
	
	// Set the CTC so we automatically compare...
	TCCR1 |= (1<<CTC1);
	// Set the CTC compare value - 75 X 4us is 300us
	OCR1C = 75;
}

/*
// This is a debugging function that toggles a given output pin x every time it's called
int toggleArray[8];
void toggleLED(uint8_t x)
{
	sbi(DDRB, x);
	if (toggleArray[x]==0 )
	{
		toggleArray[x] = 1;
		sbi(PORTB, x);
	}
	else
	{
		toggleArray[x] = 0;
		cbi(PORTB, x);
	}
}
*/



// This function gets run once per loop, and just updates the millisecond timer,
// resetting it every minute.
inline static void checkTimer0()
{
	if (TIFR & (1 << OCF0A))  // Compare overflow flag
	{
		millisecondsLow++;
		if (millisecondsLow == 0xFF)
			{ millisecondsHigh++; }

		uint16_t milliseconds = (millisecondsHigh << 8) + millisecondsLow;

		// Overflow every 60 seconds to keep behavior steady
		sbi(DDRB, 4);
		if (milliseconds >= 60000)
		{
//			toggleLED(3);
			millisecondsLow = 0;
			millisecondsHigh = 0;
		}

		TIFR = (1 << OCF0A); // clear the CTC flag (writing a logic one to the set flag clears it) 
	}
}

// transmitData(uint8_t) transmits all the data in a single
// slot of our IR data buffer, should there be data in it.
// It takes in the number of the slot on the buffer to send
// and returns a zero if the data was moved to the buffer, or 1 if there was an error.
inline static uint8_t transmitData(uint8_t bufferPosition)
{
	uint8_t packetSize = IR_Data_Buffer[bufferPosition][0];
	// Sanity Check: see if the bufferPosition is an actual position
	// and the size isn't bigger than the buffer
	//if ( ( bufferPosition < 0) ||
	if(		// DonP 20110128 - Removed to clear warning, check if < 0 is always false due to data type.
		 ( bufferPosition >= IR_BUFFER_SIZE ) ||
		 ( packetSize >= IR_PACKET_MAX_LENGTH) )
		{ return 1; }// if not, we keep the buffer position here and return 1 as an error

	// Since there was data, we'll send all of it out.
	// We use i <= packetSize since the size number just gives the size of the rest
	// of the packet, ignoring the size byte, so we need to make sure we send all the
	// data.
	for (int i = 0; i <= packetSize; i++)
	{
		usiTwiTransmitByte(IR_Data_Buffer [bufferPosition][i]);
	}	
	return 0; // No errors!
}

// This function takes in the array of ints that the IR detection code
// has been building, and actually adds it to our IR_Data buffer.
// This way, we can make sure we buffer only valid IR packets.
inline static void addDataToBuffer(uint8_t IRDataArray[], uint8_t bufferIndex)
{
	// Short packets have one format...
	if (IRDataArray[0] & 0b10000000)
	{
		IR_Data_Buffer[bufferIndex][0] = 2;
		IR_Data_Buffer[bufferIndex][1] = IRDataArray[0];
		IR_Data_Buffer[bufferIndex][2] = IRDataArray[1];
	}
	else // Long packets take another format
	{   // Add one since the length reported by the packet
		// is actually the data length - 1.
		uint8_t length = (IRDataArray[0] >> 4) + 1;
		// We need to add in the length of the 'length' byte (+1)
		IR_Data_Buffer[bufferIndex][0] = length + 1;
//		IR_Data_Buffer[bufferIndex][1] = 0;
		// Return the length data to it's original form
		IR_Data_Buffer[bufferIndex][1] = (length << 4) - 1;
		// We start filling the IR data buffer at 2 since we manually filled the first 2
		// bytes in the buffer. 
		for (int i = 2; i < (length + 2); i++)
		{
			IR_Data_Buffer[bufferIndex][i] = (IRDataArray[i-2] << 4) | (IRDataArray[i-1] >> 4);			
		}
	}
	return;
}


// This function polls the TWI buffers, and if there is data in the recieve buffer (a command),
// it sends out the appropriate response.
inline static void handleTWICommunication()
{
	if(usiTwiDataInReceiveBuffer())
        {
		// Get the data from the I2C master.
    	uint8_t data = usiTwiReceiveByte();

		// Values from 0x00 to 0x0F are a 'Change address' command
		//if (data >= 0x00 && data <= 0x0F)
		if( data <= 0x0F )		// DonP 20110128 - Removed a warning, check that >0 is always true due to data type.
		{
			uint8_t newAddress = data + 0x10;
			usiTwiSetSecondAddress(newAddress);
			eeprom_write_byte ((uint8_t*) EEPROM_ADDRESS, newAddress);
		}
		// Master is asking, 'How old is the data?'
		else if (data == 0x10)
		{
			flushTwiTransmitBuffer();
			// If the data in the millisecondsHold buffers hasn't been cleared by this function
			// earlier, send that
			if (millisecondsLowHold> 0)
			{
				usiTwiTransmitByte(millisecondsHighHold);
				usiTwiTransmitByte(millisecondsLowHold);
				millisecondsLowHold = 0;
				millisecondsHighHold = 0;
			}
			else // Otherwise, send time since last IR data was received
			{
				usiTwiTransmitByte(millisecondsHigh);
				usiTwiTransmitByte(millisecondsLow);
			}
		}
		// 0x20 is the 'Request Data' command
		else if (data == 0x20)
		{	
			// Clear the transmit buffer, so we don't chance sending old data
			// if the last transmission wasn't fully read.  This DOES NOT clear
			// the buffer of IR packets we've built up.
			flushTwiTransmitBuffer();
			// If we've got no data, or we had a packet error, transmit an error code
			if ( (IR_Data_Buffer_Size <= 0) || Error_Flag)
			{
				//usiTwiTransmitByte(0x01); // Error code: 
				usiTwiTransmitByte(0xff); // Error code should be 0xFF not 0x01 (DonP 20110125)
				if (Error_Flag)
				{
					usiTwiTransmitByte(Error_Flag); // Packet decoding error
					Error_Flag = 0;	// DonP 20110128 - clear this if it happens, otherwise never goes away until next good packet.
				}
				else
				{
					usiTwiTransmitByte(0x01); // No data ready/decoded
				}
			}
			else  // Otherwise, transmit the data we've got
			{
				Error_Flag = transmitData(IR_Data_Tail); // This returns 1 if the packet fails a
				                                         // final check..
				IR_Data_Buffer_Size--;
				advanceIRDataTail();
				// And lock in how long it was between receiving data from the IR unit and sending it
				millisecondsLowHold = millisecondsLow;
				millisecondsHighHold = millisecondsHigh;
			}
		}
	}
}

// Initialization function that sets up the proper pins
inline void setupPins()
{
	sbi(DDRB, DATA_READY_PIN); // Data ready pin set to output
	cbi(DDRB, IR_SENSOR_PIN);  // IR sensor pin set up as input
	sbi(PORTB, IR_SENSOR_PIN); // with the internal pull-up enabled
}



// This function sets up all the pins.
int main()
{
	// First, let's grab our secondary TWI address out of EEPROM
	uint8_t secondTWIAddress = 0x10;
	// Read it out from block 0
	secondTWIAddress = eeprom_read_byte((uint8_t*)EEPROM_ADDRESS);
	// Sanity check that the address in EEPROM is one of our valid values
	if (secondTWIAddress < 0x10 || secondTWIAddress > 0x1F)
	{   // if it's not, set it to 0x10
		secondTWIAddress = 0x10;
		eeprom_write_byte( (uint8_t*)EEPROM_ADDRESS, 0x10);
	}

	// Start our timers, enable interrupts, start TWI communication
	setupTimer0();
	setupTimer1();
	sei();
	usiTwiSlaveInit2(0x10, secondTWIAddress);
	// Set up some pins
    setupPins();



	// Test pattern for TWI transmission checking.
	IR_Data_Buffer [0][0] = 0x8;
	IR_Data_Buffer [0][1] = 0xd;
	IR_Data_Buffer [0][2] = 0xe;
	IR_Data_Buffer [0][3] = 0xa;
	IR_Data_Buffer [0][4] = 0xd;
	IR_Data_Buffer [0][5] = 0xb;
	IR_Data_Buffer [0][6] = 0xe;
	IR_Data_Buffer [0][7] = 0xe;
	IR_Data_Buffer [0][8] = 0xf;

    while(1)
	{
		// If we've got any data to send, let the world know by pulling the IR pin low
		if(IR_Data_Buffer_Size > 0)
			cbi(PORTB, DATA_READY_PIN);
		else
			sbi(PORTB, DATA_READY_PIN);
		// Take care of any TWI communication that needs to happen
		handleTWICommunication();
		// This block of code increments the timer and figures out the time
		// in milliseconds
		checkTimer0();

		// Update the counter1Cycles count
		if (TIFR & (1 << OCF1A))  // Compare overflow flag
		{
			counter1Cycles++;
			TIFR = (1 << OCF1A); // clear the CTC flag (writing a logic one to the set flag clears it) 
		}

		// Now we implement the IR decoder reading
		// Find out the state of the IR decoder pin
		int IRPinState = !!(PINB & (1 << IR_SENSOR_PIN));

		// This looks for a falling edge, and sets up some timers
		if ((IRPinState == 0) && (previousIRPinState == 1))
		{
			// Look for a 600us delay since the line rose from low
			// If it's too short ( < 300us ) or too long ( > 1200us ), report an error 
			if (counter1Cycles > 3 || counter1Cycles < 1)
			{
				if (dataIndex > 0) // if we started recording data
				{
					Error_Flag = 0x02;
				}
				// Reset the dataIndex to set up for the next valid packet (abandon the current packet)
				dataIndex = -1;
			}
		    // Reset Counter 0
			TCNT1 = 0;
			counter1Cycles = 0;
		}

		// When the line rises again, see how long we were down for, then do some calculatin'.
		//  - Builds a data packet in a dedicated buffer (FrameBuilderBuffer)
		//  - Figures out if it's a long or short packet
		//  - Once a full packet is read, insert the packet into our data ring buffer for transmission
		if ((IRPinState == 1) && (previousIRPinState == 0))
		{
			// If low for 600us - 0
			//if (counter1Cycles == 1 || counter1Cycles == 2)
			if (counter1Cycles >= 1 && counter1Cycles <=3)			// DonP 20110128 - more compliant
			{   // write a zero
				dataIndex++;
				FrameBuilderBuffer[dataIndex / 8] &= ~(1 << (7 - (dataIndex % 8)));
			}

			// If Low for 1200us - 1
			//if (counter1Cycles == 3 || counter1Cycles == 4)		// Original
			if (counter1Cycles >= 3 && counter1Cycles <= 5)	// DonP 20110128 - More compliant
			{   // write a one
				dataIndex++;
				FrameBuilderBuffer[dataIndex / 8] |= (1 << (7 - (dataIndex % 8)));	
			}

			// Low for 2400us - Start condition
			if (counter1Cycles >= 6 && counter1Cycles <= 8)
			{
				dataIndex = -1;
				longPacketLength = 0; // Reset the size of the long packet
			}

			// If we're low for longer than a start condition, we timed out somehow
			if (counter1Cycles > 8)
			{
				Error_Flag = 0x02;// Set the error flag
				// Reset the packet metadata
				dataIndex = -1;
				longPacketLength = 0;
			}
		
			// After getting the first bit of the IR data,we can figure
			// out if it's a long or short packet
			if (dataIndex == 1)
				{ isShortPacket = FrameBuilderBuffer[0] & (1<<7); }
		
			// If it's a long packet and we've read enough to figure out the 
			// packet length, store it.  Also, do it only once per message
			if (!isShortPacket && (dataIndex > 4) && (longPacketLength == 0))
				{ longPacketLength = (FrameBuilderBuffer[0] >> 4) + 1;  }

			// Now if we've got the right amount of data, write it to the output buffer
			if ( (!isShortPacket && (longPacketLength != 0) && (dataIndex >= (longPacketLength * 8) + 3) )
			    || (isShortPacket && (dataIndex >= 11)) )
			{
			   // If there's room in the buffer, store the data in it
			   if (!isBufferFull())
			   {   
				   // Store the complete packet in our IR data ring buffer
				   addDataToBuffer(FrameBuilderBuffer, IR_Data_Head);
				   // Advance the ring buffer to a new slot
				   advanceIRDataHead();
				   // Let us know we have one more packet of data
				   IR_Data_Buffer_Size++;
			   }
			   // Clear the error flag since we got a complete packet, even if we didn't buffer it
			   Error_Flag = 0;
			   dataIndex = -1;
			   // And reset the timer, so we can tell how long since the most recent data packet
			   millisecondsLow = 0;
			   millisecondsHigh = 0;
			}
			_delay_us(200); // For whatever reason, it needs time to write to the buffer

			// Finally, set counter1Cycles to zero so we can time the high time too
			counter1Cycles = 0;
			TCNT1 = 0; // And reset the timer to zero

		} // End of 'Rising Edge' block


    	// Now that we've read the pin, make sure that we remember what the pin state was for next time
		previousIRPinState = IRPinState; 



	} // End of main loop
    return 0;
}
